

namespace TXML
{

	//string class represents strings as a begin and end marker (half-open like STL string)
	//instead of a null terminated array (mostly, because this can "view" data without inserting 0s)
	
	//a String may contain utf8 characters, but will be oblivious to them. in particular, length() will
	//return the number of bytes in the string not the number of characters in the string.
	//equality tests are also a byte-by-byte comparison, so utf8 words that are equivalent to an (7 bit)ascii
	//character will not compare as identical.
	class String
	{
		const char* mBegin;
		const char* mEnd;
	public:
		const char* begin() const {return mBegin;}
		const char* end() const {return mEnd;}
		size_t length() const {return mEnd - mBegin;}
		bool empty() const
		{
			return length() == 0;
		}

		//various flavours of equality test
		bool equals(const String& rhs) const
		{
			return equal(*this, rhs);
		}
		bool equals(const char* rhs) const
		{
			return equal(*this, rhs);
		}
		bool equalsi(const char* rhs) const
		{
			return equali(*this, rhs);
		}
		bool startswith(const char* rhs) const
		{
			const char* p = rhs;
			const char* q = begin();
			while (*p && q < end())
			{
				if(*p != *q)
					return false;

				++p;
				++q;
			}
			return (*p == 0); //got to the end of the compare string
		}
		bool endswith(const char* rhs) const
		{
			return false;
		}

		

		String(const char* s, const char* e)
			: mBegin(s)
			, mEnd(e)
		{	}		

		String()
			: mBegin(0)
			, mEnd(0)
		{	}

		static char tolower(const char a)
		{
			return (a < 91 && a > 64) ? a + 32 : a;
				
		}

		static bool equal(const String& lhs, const String& rhs)
		{
			size_t l = lhs.length();
			if( l != rhs.length() )
				return false;

			for(size_t i=0; i<l; ++i)
				if(*(lhs.mBegin+i) != *(rhs.mBegin+i))
					return false;

			return true;
		}

		static bool equal(const String& lhs, const char* rhs)
		{
			//empty strings are equal
			if(lhs.empty())
				return *rhs == '0';

			const char* a=lhs.mBegin;
			const char* b=rhs;
			while(a < lhs.mEnd && *b)
			{
				if(*a != *b)
					return false;
				else
				{
					++a;
					++b;
				}
			}

			//strings only equal if they both end at the same place
			return (a == lhs.end() && *b == 0);
		}

		static bool equali(const String& lhs, const char* rhs)
		{
			//empty strings are equal
			if(lhs.empty())
				return *rhs == '0';

			const char* a=lhs.mBegin;
			const char* b=rhs;
			while(a < lhs.mEnd && *b)
			{
				if(tolower(*a) != tolower(*b))
					return false;
				else
				{
					++a;
					++b;
				}
			}

			//strings only equal if they both end at the same place
			return (a == lhs.end() && *b == 0);
		}
	};

	enum EParseErrors
	{
		EPE_DocumentTooDeep,
		EPE_EncodingNotSupported,
		EPE_VersionNotSupported,
		EPE_InconsistentEncoding,

		EPE_InternalError,
	};

	//interface called by Parse, derive & implement to build your xml reader.
	class Consumer
	{
	public:
		virtual void BeginDocument(const String& name, const String& ns) {};
		virtual void EndDocument() {};

		//elements, any node that isn't the document or a comment
		virtual void BeginElement(const String& name, const String& ns) {};
		virtual void ElementContents(const String& text) {};
		virtual void EndElement() {};

		//attributes can appear on any element, including processing instructions
		virtual void Attribute(const String& name, const String& ns, const String& value) {};
		
		//namespace declaration, called instead of Attribute for "xmlns:foo=uri" constructs
		virtual void NamespaceDeclaration(const String& ns, const String& uri) {};

		//raw cdata block
		virtual void CData(const String& cdata) {};

		//errors:
		virtual void ElementError(const String& elem) {};
		virtual void AttributeError(const String& attr) {};
		virtual void ParseError(EParseErrors error) {};
	};

	class Parser
	{
		static const int kMaxDepth = 64;
		String mStack[kMaxDepth];
		int mStackIdx;

		static void ProcessAttributes(const String& elem, bool bUtf8, Consumer* pConsumer);
		static void ProcessAttribute(const String& attr, bool bUtf8, Consumer* pConsumer);

	public:
		Parser()
			: mStackIdx(0)
		{	}

		void Parse(const char* pString, Consumer* pConsumer);
	};

};